#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <linux/fb.h>

#include "jpg.h"

int screensize;

void init_lcd(struct lcd_info *lcdinfo)
{
    lcdinfo->fd = open("/dev/fb0", O_RDWR);
    if(lcdinfo->fd == -1)
    {
        perror("打开LCD设备失败");
        exit(0);
    }

    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    // 获取LCD硬件参数
    ioctl(lcdinfo->fd, FBIOGET_VSCREENINFO, &vinfo);
    ioctl(lcdinfo->fd, FBIOGET_FSCREENINFO, &finfo);

    lcdinfo->width = vinfo.xres;    
    lcdinfo->height= vinfo.yres;    
    lcdinfo->bpp   = vinfo.bits_per_pixel;    
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel/8;

    // 将映射内存设置为 800 * 480
    lcdinfo->fb_mem = mmap(NULL, screensize, PROT_READ|PROT_WRITE,
                           MAP_SHARED, lcdinfo->fd, 0);
    if(lcdinfo->fb_mem == MAP_FAILED)
    {
        perror("内存映射失败");
        exit(0);
    }
}

void load_jpg(const char *jpgfile, struct jpg_info *jpginfo)
{
    FILE *fp = fopen(jpgfile, "r");

    // 读取jpg数据
    struct stat finfo;
    stat(jpgfile, &finfo);
    char *jpgdata = calloc(1, finfo.st_size);

    // 加载JPG数据
    int n, total=0, jpgsize=finfo.st_size;
    while(jpgsize > 0)
    {
        n = fread(jpgdata+total, 1, jpgsize, fp); // n就是实际读到的字节数

        jpgsize -= n;
        total   += n;
    }
    printf("total: %d\n", total);

    // 调用jpg2rgb
    jpg2rgb(jpgdata, finfo.st_size, jpginfo);
    printf("图片尺寸：%d × %d （色深%d比特）\n", jpginfo->width,
                                                jpginfo->height,
                                                jpginfo->bpp);
    fclose(fp);
}

int main(int argc, char const *argv[])
{
    // 1. 准备好LCD
    struct lcd_info lcdinfo;
    init_lcd(&lcdinfo);

    // 2. 加载jpeg图片
    // 3. 解码jpeg，得到RGB
    struct jpg_info jpginfo;
    load_jpg(argv[1], &jpginfo); // 1,读取jpeg文件内容
                                 // 2,获取jpeg图片尺寸
                                 // 3,解码得到RGB

    // 4. LCD显示出来
    for(int h=0; h<jpginfo.height && h<lcdinfo.height; h++)
    {
        int lcd_offset = lcdinfo.width * 4 * h;
        int rgb_offset = jpginfo.width * 3 * h;

        // 显示一行
        for(int w=0; w<jpginfo.width && w<lcdinfo.width ; w++)
        {
            // memcpy(lcdinfo.fb_mem + 4*w + lcd_offset, jpginfo.rgb + 3*w + rgb_offset, 3);

            memcpy(lcdinfo.fb_mem + 4*w + lcd_offset+2, jpginfo.rgb + 3*w + rgb_offset+0, 1);
            memcpy(lcdinfo.fb_mem + 4*w + lcd_offset+1, jpginfo.rgb + 3*w + rgb_offset+1, 1);
            memcpy(lcdinfo.fb_mem + 4*w + lcd_offset+0, jpginfo.rgb + 3*w + rgb_offset+2, 1);
        }
    }

    // 5. 释放相关资源
    close(lcdinfo.fd);
    munmap(lcdinfo.fb_mem, screensize);
    free(jpginfo.rgb);

    return 0;
}
